package com.seu.dm.serviceimpls;

import com.seu.dm.dto.SeckillEntryDTO;
import com.seu.dm.entities.*;
import com.seu.dm.helpers.RedisHelper;
import com.seu.dm.mappers.ProductMapper;
import com.seu.dm.mappers.SeckillMapper;
import com.seu.dm.services.*;
import org.apache.commons.lang.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;

/**
 * Created by Greeting on 2017/3/20.
 */
@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private SeckillMapper seckillMapper;

    @Autowired
    private ProductService productService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderProductService orderProductService;

    @Autowired
    private SellerService sellerService;

    @Autowired
    private CampusService campusService;




    /**
     * 混淆加密的盐值
     */
    final private String salt = "asdji9woeurhfweiutfwsdlkiuhqwi8oelhriwhriuwerwer";

    /**
     * 获得所有的秒杀项目并携带上对应的产品对象
     * @return
     */
    @Override
    public List<Seckill> queryAllWithProduct() {
        List<Seckill> seckills = seckillMapper.queryAll();
        for(Seckill seckill : seckills){
            int productId = (int)seckill.getProductId();
            Product product = productService.findProduct(productId);
            seckill.setProduct(product);
        }
        return seckills;
    }

    /**
     * 根据id获取对应的秒杀项目，并带上对应的产品对象
     * @param seckillId
     * @return
     */
    @Override
    public Seckill queryByIdWithProduct(long seckillId) {
        Seckill seckill = seckillMapper.queryById(seckillId);
        int productId = (int)seckill.getProductId();
        Product product = productService.findProduct(productId);
        seckill.setProduct(product);
        return seckill;
    }

    /**
     * 根据时间获得即将要开始的秒杀项目的列表，并带上产品对象
     * @param startTime
     * @return
     */
    @Override
    public List<Seckill> queryAllStandByByStartTimeWithProduct(Date startTime) {
        List<Seckill> seckills = seckillMapper.queryAllStandByByStartTimeWithProduct(startTime);
        for(Seckill seckill : seckills){
            int productId = (int)seckill.getProductId();
            Product product = productService.findProduct(productId);
            seckill.setProduct(product);
        }
        return seckills;
    }

    /**
     * 根据id和开始时间，获得对应的已经开始的且数量不为0的有效的秒杀项目
     * @param seckillId
     * @param startTime
     * @return
     */
    @Override
    public Seckill queryAvailableByIdAndStartTime(long seckillId,Date startTime) {
        return seckillMapper.queryAvailableByIdAndStartTime(seckillId,startTime);
    }

    @Override
    public int insert(Seckill seckill) {
        return seckillMapper.insert(seckill);
    }

    @Override
    public int update(Seckill seckill) {
        return seckillMapper.update(seckill);
    }

    @Override
    public int deleteById(long seckillId) {
        return seckillMapper.deleteById(seckillId);
    }

    /**
     * 减少库存，并生成订单，落地到数据库
     * @param seckillId
     * @return
     */
    @Override
    public int executeSeckill(long seckillId, Integer buyerId) {
        String listKey = this.getSeckillListKeyBySeckillId(seckillId);
        String setKey = this.getSeckillSetKeyBySeckillId(seckillId);
        System.out.println("begin get jedis");
        Jedis jedis = RedisHelper.getJedis();
        System.out.println("begin to check repeat");
        boolean isExist = jedis.sismember(setKey,String.valueOf(buyerId));
        if(isExist){
            jedis.close();
            //不能重复抢购
            return 2;
        }
        System.out.println("begin to lpop");
        String result = jedis.lpop(listKey);
        if(result==null){
            jedis.close();
            //没有库存了
            return 0;
        }
        //把买家id加入到缓存集合，避免二次购买
        System.out.println("begin to add to set");
        jedis.sadd(setKey,String.valueOf(buyerId));
        System.out.println("begin do transaction");
        jedis.close();
        return this.doTransaction(seckillId,buyerId);
    }


    public int doTransaction(long seckillId,Integer buyerId){
        //操作数据库
        Seckill seckill = this.getSeckillById(seckillId);
        seckillMapper.reduceNumberById(seckillId);
        OrderProduct orderProduct = new OrderProduct();
        orderProduct.setPrice(seckill.getKillPrice());
        orderProduct.setProductNum(1);
        orderProduct.setProductId(seckill.getProductId());
        orderProductService.addOrderProduct(orderProduct);
        Order order = new Order();
        order.setOrderProductId(orderProduct.getId());
        order.setStatus(1);
        order.setUserId(buyerId);
        Product product = productService.findProduct(seckill.getProductId());
        order.setSellerId(product.getSellerId());
        Seller seller = sellerService.findSeller(product.getSellerId());
        order.setCampusId(seller.getCampusId());
        order.setTotalPrice(seckill.getKillPrice());
        order.setShopId(seller.getId());
        orderService.addOrder(order);
        orderProduct.setOrderId(order.getId());
        return orderProductService.updateOrderProduct(orderProduct);
    }



    @Override
    /**
     * 根据秒杀类目的id和开始时间
     * 获得秒杀入口的封装对象
     * 这边需要调用Redis缓存
     */
    public SeckillEntryDTO getSeckillEntry(long seckillId) {
        SeckillEntryDTO seckillEntry = new SeckillEntryDTO();
        Seckill seckill = this.getSeckillById(seckillId);
        if(seckill == null){
            seckillEntry.setLeftTime(-1);
            seckillEntry.setAvailable(false);
            return seckillEntry;
        }
        if(seckill.getNumber()<=0){
            seckillEntry.setLeftTime(-1);
            seckillEntry.setAvailable(false);
            return seckillEntry;
        }
        Date date = new Date();
        if(seckill.getStartTime().getTime() > date.getTime()){
            long leftTime = seckill.getStartTime().getTime() - date.getTime();
            seckillEntry.setLeftTime(leftTime/1000);
            seckillEntry.setAvailable(false);
            return seckillEntry;
        }
        seckillEntry.setLeftTime(0);
        seckillEntry.setAvailable(true);
        seckillEntry.setSeckillId(seckill.getSeckillId());
        String md5 = getMd5(seckillId);
        seckillEntry.setMd5(md5);
        return seckillEntry;
    }


    /**
     * 获取seckill对象，并应用Redis缓存
     * 缓存失效就去数据库取，否则就直接从缓存拿
     * @param seckillId
     * @return
     */
    @Override
    public Seckill getSeckillById(long seckillId) {
        Seckill seckill = null;
        Jedis jedis = RedisHelper.getJedis();
        String seckillKey = this.getSeckillKeyBySeckillId(seckillId);
        jedis.del(seckillKey.getBytes());
        byte[] seckillBytes = jedis.get(seckillKey.getBytes());
        if(seckillBytes != null) {
            seckill = (Seckill) SerializationUtils.deserialize(seckillBytes);
            jedis.close();
            return seckill;
        }else {
            seckill = seckillMapper.queryById(seckillId);
            if(seckill == null){
                return seckill;
            }
            Product product = productService.findProduct(seckill.getProductId());
            seckill.setProduct(product);
            Seller seller = sellerService.findSeller(product.getSellerId());
            seckill.setSeller(seller);
            Campus campus = campusService.findCampus(seller.getCampusId());
            seckill.setCampusName(campus.getName());
            seckillBytes = SerializationUtils.serialize(seckill);
            //缓存时间设置为1个小时
            jedis.setex(seckillKey.getBytes(),3600,seckillBytes);
            jedis.close();
        }
        return seckill;
    }

    /**
     * 根据秒杀类目的id生成对应的md5加密字符串
     * @param seckillId
     * @return
     */
    private String getMd5(long seckillId){
        String md5 = String.valueOf(seckillId) + salt;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(md5.getBytes());
            return new BigInteger(1, md.digest()).toString(16);
        }catch(NoSuchAlgorithmException e){
            System.out.println("没有MD5加密算法");
        }
        //发生异常的话就返回不加密的字符串用于应急
        return "md5"+String.valueOf(seckillId);
    }

    /**
     * 发布秒杀项目
     * 做好秒杀队列
     * 增加秒杀项目
     * 更新产品
     * @param seckill
     * @param product
     * @return
     */
    @Override
    public int publishSeckill(Seckill seckill, Product product){
        int tempNumber = seckill.getNumber();
        seckill.setNumber(0);
        this.insert(seckill);
        seckill.setNumber(tempNumber);
        String listKey = this.getSeckillListKeyBySeckillId(seckill.getSeckillId());
        Jedis jedis = RedisHelper.getJedis();
        for(int i = 0; i < seckill.getNumber(); ++i){
            jedis.lpush(listKey,"true");
        }
        jedis.close();
        productService.updateProduct(product);


        return this.update(seckill);
    }

    /**
     * 获取缓存列表key
     * @param seckillId
     * @return
     */
    private String getSeckillListKeyBySeckillId(long seckillId){
        return "seckillList:"+String.valueOf(seckillId);
    }

    /**
     * 获取缓存集合key
     * @param seckillId
     * @return
     */
    private String getSeckillSetKeyBySeckillId(long seckillId){
        return "seckillSet:"+String.valueOf(seckillId);
    }

    /**
     * 获取缓存key
     * @param seckillId
     * @return
     */
    private String getSeckillKeyBySeckillId(long seckillId){
        return "seckill:"+String.valueOf(seckillId);
    }

}
